package principal;

import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class Vertice {

    public static final int ALT = 4;
    public static final int LARG = 4;

    private byte[][] puzzle = new byte[ALT][LARG];
    private final Posicao vazio;
    private Vertice pai;
    private int g;
    private int h;

    public Vertice(byte[][] puzzle) {
        this.puzzle = puzzle;
        this.vazio = busca(this.puzzle, 0);
    }

    public Vertice(byte[][] puzzle, Posicao vazio, Vertice pai) {
        this.puzzle = puzzle;
        this.vazio = vazio;
        this.pai = pai;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        DecimalFormat df = new DecimalFormat("00  ");
        for (int i = 0; i < ALT; i++) {
            for (int j = 0; j < LARG; j++) {
                sb.append(df.format(puzzle[i][j]));
            }
            sb.append("\n");
        }
        return sb.toString();
    }

    public Integer chave() {
        return hashCode();
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 31 * hash + Arrays.deepHashCode(this.puzzle);
        return hash;
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Vertice)) {
            return false;
        }

        Vertice v = (Vertice) o;

        for (int i = 0; i < ALT; i++) {
            for (int j = 0; j < LARG; j++) {
                if (this.puzzle[i][j] != v.puzzle[i][j]) {
                    return false;
                }
            }
        }

        return true;
    }

    public byte[][] getPuzzle() {
        return puzzle;
    }

    public Vertice getPai() {
        return pai;
    }

    public int getF() {
        return this.g + this.h;
    }

    public int getG() {
        return g;
    }

    public void setH(int h) {
        this.h = h;
    }

    public static Posicao busca(byte[][] puzzle, int valor) {
        for (int i = 0; i < Vertice.ALT; i++) {
            for (int j = 0; j < Vertice.LARG; j++) {
                if (puzzle[i][j] == valor) {
                    return new Posicao(i, j);
                }
            }
        }
        return null;
    }

    private byte[][] copiaPuzzle() {
        byte[][] copia = new byte[ALT][LARG];

        for (int i = 0; i < ALT; i++) {
            for (int j = 0; j < LARG; j++) {
                copia[i][j] = puzzle[i][j];
            }
        }

        return copia;
    }

    private void inverte(Posicao p1, Posicao p2) {
        byte temp = puzzle[p1.linha][p1.coluna];
        puzzle[p1.linha][p1.coluna] = puzzle[p2.linha][p2.coluna];
        puzzle[p2.linha][p2.coluna] = temp;
    }

    // generalização
    private Vertice move(Posicao p) {
        Vertice movido = new Vertice(this.copiaPuzzle(), p, this);
        movido.inverte(this.vazio, p);
        movido.g = this.g + 1;

        return movido;
    }

    private Vertice moveUp() {
        return move(new Posicao(vazio.linha + 1, vazio.coluna));
    }

    private Vertice moveDown() {
        return move(new Posicao(vazio.linha - 1, vazio.coluna));
    }

    private Vertice moveLeft() {
        return move(new Posicao(vazio.linha, vazio.coluna + 1));
    }

    private Vertice moveRight() {
        return move(new Posicao(vazio.linha, vazio.coluna - 1));
    }

    public Set<Vertice> geraSucessores() {
        Set<Vertice> sucessores = new HashSet<>();

        if (vazio.linha > 0) {
            sucessores.add(moveDown());
        }
        if (vazio.linha < (ALT - 1)) {
            sucessores.add(moveUp());
        }
        if (vazio.coluna > 0) {
            sucessores.add(moveRight());
        }
        if (vazio.coluna < (LARG - 1)) {
            sucessores.add(moveLeft());
        }

        return sucessores;
    }
}
